JavaScript 执行机制与EventLoop

JavaScript 执行机制与EventLoop

JavaScript的执行机制

  1. 同步和异步任务进入不同的’执行场所’.
  2. 同步进入主线程,形成一个执行栈(后进先出)
  3. 异步进入 EventTable 并注册函数.当异步任务有了运行结果,将函数移入 EventQueue.(先进先出)
  4. 主线程任务执行完毕为空,将 EventQueue 读取相应函数进入主线程执行.
  5. 上述过程不断循环,即 EventLoop(事件循环)
    1. 简单概括就是,js 只有一个主线程,同步代码会依次入栈,执行完出栈,待栈为空时去检查异步的任务队列,
    2. 如果异步事件触发,则将其加入到主线程的执行栈,不断循环.

宏任务和微任务

宏任务(macrotask)和微任务(microtask)是异步任务的两种分类,首先取出宏任务执行然后取出微任务执行,不断循环,直到EventQueue为空.需要注意的是,在当前的微任务没有执行完成时,是不会执行下一个宏任务的。
宏任务: 整体代码script,setTimeOut,setInterval,setImmediate,requestAnimationFrame
微任务: promise,then,catch,finally,process.nexttick,MutationObserver

代码分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
console.log('1')
setTimeout(function(){
console.log('2')
process.nextTick(function(){
console.log('3')
})
new Promise(function(resolve){
console.log('4')
resolve()
}).then(function(){
console.log('5')
})
})
process.nextTick(function(){
console.log('6')
})
new Promise(function(resolve){
console.log('7')
resolve()
}).then(function(){
console.log('8')
})
setTimeout(function(){
console.log('9')
process.nextTick(function(){
console.log('10')
})
new Promise(function(resolve){
console.log('11')
resolve()
}).then(function(){
console.log('12')
})
})

代码解析:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
第一轮事件循环: 
整体 script 作为第一个宏任务进入主线程,然后输出1.
然后遇到 setTimeout,其回调分发到宏任务 EveneQueue 中,暂记为 setTimeout1
process.nextTick分发到微任务 EveneQueue 中暂记为 process1
promise 和 new Promise 直接执行输入7,then 分发到微任务,记为 then1
然后是setTimeout 记为setTimeout2,分发到宏任务EveneQueue
第一轮宏任务执行完毕,执行微任务队列,把微任务process1,then1加入执行栈,分别输出6,8
第一轮执行完毕,分别输出 1 => 7 => 6 => 8
第二轮事件循环:
开启第二轮宏任务setTimeout1,输出2
process和 then 分发到微任务,记为 process2,then2.
然后输出4,宏任务执行完毕.
微任务process2,then2加入执行栈,输出3,5
第二轮执行完毕,分别输出 2 => 4 => 3 => 5
第三轮时间循环:
开启第三轮宏任务setTimeout2,输出 9
process和 then 分发到微任务,记为 process3,then3.
然后输出11,宏任务执行完毕.
任务process3,then3加入执行栈,输出10,12
第三轮执行完毕,分别输出 9 => 11 => 10 => 12